/* arch/x86/kernel/arch_int.c 
 * 
 * Copyright (C) 2021, 2022 intirain. 
 *
 * This file is part of evx. 
 * 
 * evx is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version. 
 * 
 * evx is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with evx. If not, see <https://www.gnu.org/licenses/>
 */ 




#include <kernel/init.h>
#include <kernel/module.h>
#include <kernel/mm.h>
#include <kernel/kernel.h>
#include <kernel/kmlog.h>

#include <arch/archinfo.h>
#include <arch/mm.h>
#include <arch/asm.h>
#include <arch/int.h>
#include <arch/string.h>

#include <sys/types.h>
#include <config.h>


/* head$(BITS).S */
/* extern struct idt_entry *idt; things interesting.
 * a struct idt_entry *idt, and that is a pointer. idt, is 
 * the address.
 *
 * a struct idt_entry idt[], and that is an array. */

extern struct idt_entry idt[256];

extern void ret_intr(void);
int do_trap(int nr, __archptr_t sp);
static int set_gate(struct idt_entry *, void (*) (void), int attr);

#define set_int_gate(nr, handler) 	set_gate(idt + nr, handler, 0x8e)
#define set_trap_gate(nr, handler) 	set_gate(idt + nr, handler, 0x8f)
#define set_sys_gate(nr, handler) 	set_gate(idt + nr, handler, 0xef)

#if CONFIG_64BIT

/* errcodes later */

__asm__ (
		".globl common_trap\n\t"
	"common_trap:\n\t"
	/* rax already saved */
		"pushq %rcx\n\t"
		"pushq %rbx\n\t"
		"pushq %rdx\n\t"
		"pushq %rbp\n\t"
		"pushq %rsi\n\t"
		"pushq %rdi\n\t"
		"pushq %r8\n\t"
		"pushq %r9\n\t"
		"pushq %r10\n\t"
		"pushq %r11\n\t"
		"pushq %r12\n\t"
		"pushq %r13\n\t"
		"pushq %r14\n\t"
		"pushq %r15\n\t"
		"movq %rax, %rdi\n\t"

		"movq %ds, %rax\n\t"
		"pushq %rax\n\t"
		"movq %es, %rax\n\t"
		"pushq %rax\n\t"
		"movq %fs, %rax\n\t"
		"pushq %rax\n\t"
		"movq %gs, %rax\n\t"
		"pushq %rax\n\t"

		"movq $0x10, %rax\n\t"
		"movq %rax, %ds\n\t"
		"movq %rax, %es\n\t"
		"movq %rax, %fs\n\t"
		"movq %rax, %gs\n\t"
		"movq %rax, %ss\n\t"

		"movq %rsp, %rsi\n\t"
		"pushq ret_intr(%rip)\n\t"
		"jmp do_trap\n\t"
);

#define build_trap_entry_errcode(nr) 		\
	void trap_##nr##_entry(void); 		\
	__asm__ (				\
		".globl trap_"#nr"_entry\n\t" 	\
		"trap_"#nr"_entry:\n\t"		\
		"cli\n\t"			\
		"pushq %rax\n\t"		\
		"movq $"#nr", %rax\n\t"		\
		"jmp common_trap\n\t"		\
	);

#define build_trap_entry_nerrcode(nr) 		\
	void trap_##nr##_entry(void);		\
	__asm__ (				\
		".globl trap_"#nr"_entry\n\t"	\
		"trap_"#nr"_entry:\n\t"		\
		"cli\n\t"			\
		"pushq $0\n\t"			\
		"pushq %rax\n\t"		\
		"movq $"#nr", %rax\n\t"		\
		"jmp common_trap\n\t"		\
	)
#else

__asm__ (
		".globl common_trap\n\t"
	"common_trap:\n\t"
		"pushl %ecx\n\t"
		"pushl %ebx\n\t"
		"pushl %edx\n\t"
		"pushl %ebp\n\t"
		"pushl %esi\n\t"
		"pushl %edi\n\t"

		"movl %eax, %edi\n\t"
		"movl %ds, %eax\n\t"
		"pushl %eax\n\t"
		"movl %es, %eax\n\t"
		"pushl %eax\n\t"
		"movl %fs, %eax\n\t"
		"pushl %eax\n\t"
		"movl %gs, %eax\n\t"
		"pushl %eax\n\t"

		"movl $0x10, %eax\n\t"
		"movl %eax, %ds\n\t"
		"movl %eax, %es\n\t"
		"movl %eax, %fs\n\t"
		"movl %eax, %gs\n\t"
		"movl %eax, %ss\n\t"

		"movl %esp, %esi\n\t"
		"pushl %esi\n\t"
		"pushl %edi\n\t"
		"call do_trap\n\t"
		"addl $8, %esp\n\t"
		"jmp ret_intr\n\t"
);

#define build_trap_entry_errcode(nr) 		\
	void trap_##nr##_entry(void); 		\
	__asm__ (				\
		".globl trap_"#nr"_entry\n\t"	\
		"trap_"#nr"_entry:\n\t"		\
		"cli\n\t"			\
		"pushl %eax\n\t"		\
		"movl $"#nr", %eax\n\t"		\
		"jmp common_trap\n\t"		\
	);

#define build_trap_entry_nerrcode(nr) 		\
	void trap_##nr##_entry(void);		\
	__asm__ (				\
		".globl trap_"#nr"_entry\n\t"	\
		"trap_"#nr"_entry:\n\t"		\
		"cli\n\t"			\
		"pushl $0\n\t"			\
		"pushl %eax\n\t"		\
		"movl $"#nr", %eax\n\t"		\
		"jmp common_trap\n\t"		\
	)

#endif


build_trap_entry_nerrcode(0);
build_trap_entry_nerrcode(1);
build_trap_entry_nerrcode(2);
build_trap_entry_nerrcode(3);
build_trap_entry_nerrcode(4);
build_trap_entry_nerrcode(5);
build_trap_entry_nerrcode(6);
build_trap_entry_nerrcode(7);
build_trap_entry_errcode(8);
build_trap_entry_nerrcode(9);
build_trap_entry_errcode(10);
build_trap_entry_errcode(11);
build_trap_entry_errcode(12);
build_trap_entry_errcode(13);
build_trap_entry_errcode(14);
build_trap_entry_nerrcode(15);
build_trap_entry_nerrcode(16);
build_trap_entry_errcode(17);
build_trap_entry_nerrcode(18);
build_trap_entry_nerrcode(19);
build_trap_entry_nerrcode(20);

/* set the handlers for traps, 0 - 31 */

int __init arch_traps_init(void) {
#include "__arch_traps_generated.c"

	return 0;
}

const static char *trap_msg[32] = {
	"division by zero", 
	"debug trap", 
	"NMI", 
	"breakpoint", 
	"overflow", 
	"bound", 
	"invalid op-code", 
	"device not avaliable", 
	"double fault", 
	"coprocessor segment overrun", 
	"invalid tss", 
	"segment not present", 
	"stack-segment fault", 
	"general protection", 
	"page fault", 
	"reserved", 
	"x87 floating-point exception", 
	"alignment check", 
	"machine check", 
	"simd floating-point exception", 
	"virtualization exception", 
	"control protection exception", 
	"reserved", 
	"hypervisor injection exception", 
	"vmm communication exception", 
	"security exception", 
	"reserved"
};


int do_trap(int nr, __archptr_t sp) {
	struct registers *regs = (struct registers *) sp;

	kmlog(KERN_ABANDON "Oops! trap %d: %s\n", nr, trap_msg[nr]);
	kmlog(KERN_ABANDON "sp at %p\n", sp);

	/* okay, we use wrong tabs in protected-mode but i think that's ok :-) */
	kmlog(KERN_ABANDON "ax\t\t\tcx\t\t\tbx\n");
	kmlog(KERN_ABANDON "%p\t%p\t%p\n", regs->ax, regs->cx, regs->bx);
	kmlog(KERN_ABANDON "dx\t\t\tbp\t\t\tsp\n");
	kmlog(KERN_ABANDON "%p\t%p\t%p\n", regs->dx, regs->bp, regs->sp);

#if CONFIG_64BIT
	kmlog(KERN_ABANDON "si\t\t\tdi\t\t\tr8\n");
	kmlog(KERN_ABANDON "%p\t%p\t%p\n", regs->si, regs->di, regs->r8);

	kmlog(KERN_ABANDON "r9\t\t\tr10\t\t\tr11\n");
	kmlog(KERN_ABANDON "%p\t%p\t%p\n", regs->r9, regs->r10, regs->r11);
	kmlog(KERN_ABANDON "r12\t\t\tr13\t\t\tr14\n");
	kmlog(KERN_ABANDON "%p\t%p\t%p\n", regs->r12, regs->r13, regs->r14);
	kmlog(KERN_ABANDON "r15\n%d\n", regs->r15);

#else
	kmlog(KERN_ABANDON "si\t\t\tdi\n");
	kmlog(KERN_ABANDON "%p\t%p\n", regs->si, regs->di);
#endif
	kmlog(KERN_ABANDON "ds: \t\t%p\n", regs->ds);
	kmlog(KERN_ABANDON "ss: \t\t%p\n", regs->ss);
	kmlog(KERN_ABANDON "flags: \t\t%p\n", regs->flags);
	kmlog(KERN_ABANDON "cs:ip: \t\t%d:%p\n", regs->cs, regs->ip);

	for (;;) 
		cpu_relax();

	return 0;
}


static int set_gate(struct idt_entry *entry, void (* handler) (void), int attr) {
	/* no ist support in long-mode, well, that's not so useful */
	__archptr_t p = (__archptr_t) handler;

	entry->off_0 = p & 0xffffu;
	entry->seg = 8;
#if CONFIG_64BIT
	entry->ist =
#else
	entry->resv = 
#endif
		0;

	entry->attr = attr;
	entry->off_1 = (p >> 16u) & 0xffffu;

#if CONFIG_64BIT
	entry->off_2 = p >> 32u;
	entry->zero = 0;
#endif

	return 0;
}


arch_init(arch_traps_init);

